package hotel.control;

import hotel.dao.ApartamentoDao;
import hotel.dao.ContaDao;
import hotel.dao.EntradaDao;
import hotel.dao.HospedeDao;
import hotel.dao.ItemContaDao;
import hotel.dao.ServicoDao;
import hotel.dao.TipoApartamentoDao;
import hotel.mensagens.Login;
import hotel.mensagens.ManterConta;
import hotel.mensagens.ManterHospede;
import hotel.mensagens.ManterItemConta;
import hotel.mensagens.ManterReserva;
import hotel.mensagens.ManterServico;
import hotel.mensagens.ManterTipoApartamento;
import hotel.mensagens.Mensagem;
import hotel.mensagens.TipoMensagem;
import hotel.tipos.Apartamento;
import hotel.tipos.Conta;
import hotel.tipos.Hospede;
import hotel.tipos.ItemConta;
import hotel.tipos.Registro;
import hotel.tipos.Reserva;
import hotel.tipos.Servico;
import hotel.tipos.TipoApartamento;
import hotel.userCtl.Usuario;
import hotel.userCtl.UsuarioDao;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import o2b.IStream;
import o2b.MarshallableException;
import o2b.Stream;

public class ClientRequest implements Runnable{

	Socket socket;
	
	public ClientRequest(Socket socket) {
		setSocket(socket);
	}

	@Override
	public void run() {
		processRequest();
		
	}
	
	public void processRequest(){
		// Cria as streams de entrada e saída
		InputStream is = null;
		OutputStream os = null;
		
		// Tentar setar as streams
		try {
			is = getSocket().getInputStream();
			os = getSocket().getOutputStream();
		} catch (IOException e2) {
			e2.printStackTrace();
		}
		// Cria um output stream de array de bytes
		ByteArrayOutputStream bytesCliente = new ByteArrayOutputStream();

		int aux;
		try {
			// Cria um buffered Reader e tentar ler uma linha do InputStream (no caso o tamanho da mensagem a ser recebida)
			BufferedReader br = new BufferedReader(new InputStreamReader(is));
			String resp = br.readLine();
			Integer k = Integer.parseInt(resp);
			
			int count = 0;
			// Enquanto a mensagem não tiver acabado, lê e guarda no bytesCliente
			while(count < k){
				aux = br.read();
				// System.out.println("Lendo dados enviados pelo cliente");
				bytesCliente.write(aux);
				count++;
			}
			
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		// Transforma bytesCliente em um array de bytes
		byte[] dadosCliente = bytesCliente.toByteArray();

		// Cria um novo stream e preeche com os bytes de dadosCliente
		IStream stream = new Stream();
		stream.fill(dadosCliente);

		// Cria um nova mensagem
		Mensagem mensagem = new Mensagem();
		
		// Tenta ler o tipo da mensagem
		try {
			mensagem.unmarshal(stream);
		} catch (MarshallableException e1) {
			e1.printStackTrace();
		}
		
		// Cria outro stream para enviar a resposta para o cliente
		IStream outstream = new Stream();
		
		// Avalia a mensagem recebida
		switch (mensagem.getTipo()) {
			// Trata das mensagens de login
			case TipoMensagem.LOGIN:
				Login login = new Login();
				try {
					login.unmarshal(stream);
				
					Usuario user = login.getUser();
					
					// System.out.println(" Login recebido: "+user.getUserName()+" , senha:"+user.getPassword());
	
					if (UsuarioDao.checkPassword(user)) {
						System.out.println("Login realizado com sucesso!");
	
						Mensagem msg = new Mensagem(TipoMensagem.OK);
						try {
							msg.marshal(outstream);
							os.write(outstream.getBytes());
						} catch (IOException e) {
							e.printStackTrace();
						}  catch (MarshallableException e) {
							e.printStackTrace();
						}
						
					} else {
						System.out.println("Falha no login");
	
						Mensagem msg = new Mensagem(TipoMensagem.NOK);
						try {
							msg.marshal(outstream);
							os.write(outstream.getBytes());
						} catch (MarshallableException e) {
							e.printStackTrace();
						} catch (IOException ioexp){
							ioexp.printStackTrace();
						}
						
					}
				} catch (MarshallableException e) {
					e.printStackTrace();
				}
				break;
			
			// Trata das mensagens de busca de serviço
			/*****************************************************************/
			/*****************************************************************/
			/************** Mensagem do tipo Manter Serviço ******************/
			/*****************************************************************/
			/*****************************************************************/
			case TipoMensagem.MANTERSERVICO:
				// System.out.println("MSG MANTER SERVICO");
				Boolean foundIt=false;
				ManterServico bs = new ManterServico();
				try {
					bs.unmarshal(stream);
				} catch (MarshallableException me){
					me.printStackTrace();
				}
				
				// System.out.println("Subtipo: "+bs.getTipoManterServico());
				
				switch(bs.getTipoManterServico()){
					case 1:
						Servico serviceToInclude = bs.getServico();
						//serviceToInclude.setDescricao(mis.getServico().getDescricao());
						//serviceToInclude.setPreco(mis.getServico().getPreco());
						int retorno = ServicoDao.create(serviceToInclude);
						if(retorno == -1){
							bs.setTipoManterServico(12);
							try {
								bs.marshal(outstream);
								os.write(outstream.getBytes());
							} catch (IOException e) {
								e.printStackTrace();
							}  catch (MarshallableException e) {
								e.printStackTrace();
							}
							System.out.println("Erro ao inserir serviço");
						} else {
							System.out.println("Serviço incluído com sucesso.");
							// Mensagem msg = new Mensagem(TipoMensagem.OK);
							bs.setTipoManterServico(11);
							try {
								bs.marshal(outstream);
								os.write(outstream.getBytes());
							} catch (IOException e) {
								e.printStackTrace();
							}  catch (MarshallableException e) {
								e.printStackTrace();
							}
						}
						break;
					case 2:
					// Procura o serviço pela descrição
					List<Servico> ls = ServicoDao.buscarServico(bs.getServicoBusca());
						if(ls.size() == 0){
						//	ls = ServicoDao.buscarServicoId(bs.getServico().getServiceId());
							//if(ls.size() == 0){
									foundIt = false;						
							//} else { foundIt = true;}
						} else {foundIt = true;}
						if(foundIt) {
							System.out.println("Foram encontrados "+ls.size()+" Servicos!");
							bs.setTipoManterServico(21);
							bs.setServico(ls.get(0));
							bs.setFound(1);
							try {
								bs.marshal(outstream);
								os.write(outstream.getBytes());
							} catch (MarshallableException e) {
								e.printStackTrace();
							} catch (IOException e) {
								e.printStackTrace();
							}
						} else {
							System.out.println("Servico não encontrado!");
							bs.setTipoManterServico(22);
							bs.setFound(0);
							bs.setServico(new Servico());
							bs.setTipo(0);
							try{
								bs.marshal(outstream);
								os.write(outstream.getBytes());
							} catch (MarshallableException e) {
								e.printStackTrace();
							} catch (IOException e) {
								e.printStackTrace();
							}
						}
						
						break;
						
					case 3:
						Servico serviceToDelete = bs.getServico();
						System.out.println("ServiceId: "+bs.getServico().getServiceId());
						int retorno__ = ServicoDao.delete(serviceToDelete);
						if(retorno__ == 0){
							bs.setTipoManterServico(32);
							try {
								bs.marshal(outstream);
								os.write(outstream.getBytes());
							} catch (IOException e) {
								e.printStackTrace();
							}  catch (MarshallableException e) {
								e.printStackTrace();
							}
						} else {
							bs.setTipoManterServico(31);
							try {
								bs.marshal(outstream);
								os.write(outstream.getBytes());
							} catch (IOException e) {
								e.printStackTrace();
							}  catch (MarshallableException e) {
								e.printStackTrace();
							}
						}
						break;
						
					case 4:
						Servico serviceToUpdate = bs.getServico();
						//serviceToInclude.setDescricao(mis.getServico().getDescricao());
						//serviceToInclude.setPreco(mis.getServico().getPreco());
						int retorno_ = ServicoDao.create(serviceToUpdate);
						if(retorno_ == 0){
							bs.setTipoManterServico(42);
							try {
								bs.marshal(outstream);
								os.write(outstream.getBytes());
							} catch (IOException e) {
								e.printStackTrace();
							}  catch (MarshallableException e) {
								e.printStackTrace();
							}
							System.out.println("Erro ao inserir serviço");
						} else {
							System.out.println("Serviço incluído com sucesso.");
							// Mensagem msg = new Mensagem(TipoMensagem.OK);
							bs.setTipoManterServico(41);
							try {
								bs.marshal(outstream);
								os.write(outstream.getBytes());
							} catch (IOException e) {
								e.printStackTrace();
							}  catch (MarshallableException e) {
								e.printStackTrace();
							}
						}
						break;
						
					case 5:
						List<Servico> listaServicos = ServicoDao.listarServicos();
						Vector<Servico> s_ = new Vector<Servico>(listaServicos.size());
						for (int w = 0; w < listaServicos.size(); w++){
							s_.add(listaServicos.get(w));
							System.out.println("vez!");
						}
						// Cria uma menasgem de resposta
						ManterServico ms_ = new ManterServico(51);
						// Preenche os servicos da mensagem
						ms_.setvServicos(s_);
						// Envia a mensagem
						try{
							ms_.marshal(outstream);
							os.write(outstream.getBytes());
						} catch (IOException e){
							e.printStackTrace();
						} catch (MarshallableException m){
							m.printStackTrace();
						}
						
						break;
						
					default:
						break;
				}
				break;
				
				
			// Trata as mensagens do tipo manter reserva
			/*****************************************************************/
			/*****************************************************************/
			/************** Mensagem do tipo Manter Reserva ******************/
			/*****************************************************************/
			/*****************************************************************/
			case TipoMensagem.MANTERRESERVA:
				ManterReserva mr = new ManterReserva();
			try {
				// Extrai os dados do stream para o ManterReserva mr
				mr.unmarshal(stream);
				switch(mr.getTipoManterReserva()){
				
				/*****************************************************************/
				/*****************************************************************/
				/************ Mensagem do tipo Cadastrar Reserva *****************/
				/*****************************************************************/
				/*****************************************************************/
					case 1:
						// Verificacao de datas - Data de inicio é depois da data final (fazer no cliente?)
						if(mr.getReserva().getDataInicio().after(mr.getReserva().getDataFim()))
							mr.setTipoManterReserva(12);
						// Se a data da reserva é anterior à data atual
						else if(mr.getReserva().getDataInicio().before(new Date())) {
							mr.setTipoManterReserva(12);
						} else {
							boolean livre = false;
							try { livre = EntradaDao.checkOcupation(mr.getReserva());}
							catch (NullPointerException npe){ livre = false; }
							if(livre){
								// Checa se o hospede existe:
								Hospede hospedeDaReserva = HospedeDao.buscarHospedeCPF(mr.getReserva().getHospedeId().getCpf());
								// System.out.println("CPF recebido: "+mr.getReserva().getHospedeId().getCpf()+"Achei isto: "+hospedeDaReserva.getNome());
								if(hospedeDaReserva != null){
									mr.getReserva().setHospedeId(hospedeDaReserva);
									// TODO
									// Verificar a seguir se o valor retornado não foi null
									Apartamento ap = (Apartamento) ApartamentoDao.apartamentoNumero(mr.getReserva().getAp().getNumero());
									mr.getReserva().setAp(ap);
									if(mr.getReserva().getAp() != null){
										try {
											EntradaDao.create(mr.getReserva());
										} catch (Exception e) {
											e.printStackTrace();
											mr.setTipoManterReserva(12);
											System.out.println("Deu Erro!");
											break;
										}
										// Altera a mensagem para envio
										mr.setTipoManterReserva(11);
										System.out.println("Deu certo");
									} else {
										System.out.println("Deu erro no Ap");
										mr.setTipoManterReserva(12);
									}
								} else {
									System.out.println("Deu erro no Hospede");
									mr.setTipoManterReserva(12);
								}
							} else {
								System.out.println("Deu erro na data");
								mr.setTipoManterReserva(13);
							}
							
						}
							
						// põe os dados da mensagem de resposta no outstream
						mr.marshal(outstream);
					try {
						// envia os dados ao cliente
						os.write(outstream.getBytes());
					} catch (IOException e) {
						e.printStackTrace();
					}
						break;
						
						
					/*****************************************************************/
					/*****************************************************************/
					/************ Mensagem do tipo Procurar Reserva ******************/
					/*****************************************************************/
					/*****************************************************************/
					case 2:
						Reserva r = mr.getReserva();
						// System.out.println("Vou procurar isto: "+r.getHospedeId().getCpf()+", "+r.getDataInicio());
						r = EntradaDao.procuraReserva(r);
						// if(r != null) System.out.println("Resultado da busca: "+r.getHospedeId().getNome()+", "+r.getAp().getNumero()+", "+r.getDataInicio()+", "+r.getDataFim());
						if (r != null){
							mr = new ManterReserva(21);
							//mr.setTipoManterReserva(21);
							mr.setReserva(r);
						} else {
							mr = new ManterReserva(22);
							//mr.setTipoManterReserva(22);
						}
						
						try {
							// Coloca os dados da resposta no output stream
							mr.marshal(outstream);
							// Envia os dados
							os.write(outstream.getBytes());
						} catch (IOException e) {
							e.printStackTrace();
						} catch (MarshallableException me){
							me.printStackTrace();
						}
						
						break;
						
					/*****************************************************************/
					/*****************************************************************/
					/************* Mensagem do tipo Efetivar Reserva *****************/
					/*****************************************************************/
					/*****************************************************************/
					case 5:
						Registro reg = new Registro();
						mr.setReserva(EntradaDao.procuraReserva(mr.getReserva()));
						System.out.println("Verificado: "+mr.getReserva().getAp().getTipo().getDescricao());
						reg.setAp(mr.getReserva().getAp());
						// reg.getAp().setTipo(TipoApartamentoDao.buscarAp(reg.getAp().getTipo().getId()));
						reg.setDataInicio(mr.getReserva().getDataInicio());
						reg.setDataFim(mr.getReserva().getDataFim());
						reg.setConta(new Conta());
						reg.setHospedeId(mr.getReserva().getHospedeId());
						if(EntradaDao.delete(mr.getReserva())){
							try{
								EntradaDao.create(reg);
								mr = new ManterReserva(51);
								mr.marshal(outstream);
								os.write(outstream.getBytes());
							} catch (MarshallableException me) {
								me.printStackTrace();
							} catch (IOException io){
								io.printStackTrace();
							} catch (Exception e) {
								e.printStackTrace();
								mr = new ManterReserva(52);
								mr.marshal(outstream);
								try{
									os.write(outstream.getBytes());
								} catch (IOException ioe){
									ioe.printStackTrace();
								}
							}
						}
						
						break;
				}
			} catch (MarshallableException e) {
				e.printStackTrace();
			}
				break;
				
			// Trata as mensagens do tipo manter hóspede
			/*****************************************************************/
			/*****************************************************************/
			/************** Mensagem do tipo Manter Hóspede ******************/
			/*****************************************************************/
			/*****************************************************************/
			case TipoMensagem.MANTERHOSPEDE:
				// Cria uma nova mensagem do tipo ManterHospede
				ManterHospede mh = new ManterHospede();
				// Tentar ler os dados recebiso para a variável acima
				try {
					mh.unmarshal(stream);
				} catch (MarshallableException e) {
					e.printStackTrace();
				}
				
				// Avalia o subtipo
				switch(mh.getTipoManterHospede()){
					case 1:
						// Salva o Hóspede
						int r = HospedeDao.create(mh.getHospede());
						// Trata o resultado da operação
						if(r != -1) {mh = new ManterHospede(11);} else mh = new ManterHospede(12);
						
						// Prepara a mensagem de resposta
						try {
							mh.marshal(outstream);
						} catch (MarshallableException e) {
							e.printStackTrace();
						}
						
						// Envia a mensagem de resposta
						try {
							os.write(outstream.getBytes());
						} catch (IOException e) {
							e.printStackTrace();
						}
						
						break;
						
					case 11:
						break;
					
					case 2:
						String cpf_ = null;
						try{
							cpf_ = mh.getHospede().getCpf();
						}
						catch( NullPointerException npe ){
							// Nao faz, nada, nao foi digitado um cpf
						}
						if( cpf_ != null){
							Hospede h = HospedeDao.buscarHospedeCPF(mh.getHospede().getCpf());
							if(h == null){
								List<Hospede> l1 = HospedeDao.buscarHospede(mh.getHospede().getNome());
								if(l1.size() == 0){
									System.out.println("Hospede não encontrado.");
									mh.setTipoManterHospede(22);
								} else {
									mh.setHospede(l1.get(0));
									mh.setTipoManterHospede(21);
									System.out.println("Hospede encontrado. "+mh.getHospede().getNome());
								}
							} else {
								mh.setHospede(h);
								mh.setTipoManterHospede(21);
								System.out.println("Hospede encontrado. "+mh.getHospede().getNome());
							}
						} else {
							List<Hospede> l1 = HospedeDao.buscarHospede(mh.getHospede().getNome());
							if(l1.size() == 0){
								System.out.println("Hospede não encontrado.");
								mh.setTipoManterHospede(22);
							} else {
								mh.setHospede(l1.get(0));
								mh.setTipoManterHospede(21);
								System.out.println("Hospede encontrado. "+mh.getHospede().getNome());
							}	
						}
							// Prepara a mensagem de resposta
							try {
								mh.marshal(outstream);
							} catch (MarshallableException e) {
								e.printStackTrace();
							}
							
							// Envia a mensagem de resposta
							try {
								os.write(outstream.getBytes());
							} catch (IOException e) {
								e.printStackTrace();
							}
						break;
						
					default:
						break;
				}
				
				break;
				
				
			/*****************************************************************/
			/*****************************************************************/
			/************ Mensagem do tipo Manter Apartamento ****************/
			/*****************************************************************/
			/*****************************************************************/
			case TipoMensagem.MANTERTIPOAPARTAMENTO:
				ManterTipoApartamento mta = new ManterTipoApartamento();
				try {
					mta.unmarshal(stream);
				} catch (MarshallableException e) {
					e.printStackTrace();
				}
				
				switch(mta.getTipoManterTipoApartamento()){
					/*****************************************************************/
					/*****************************************************************/
					/************ Mensagem do tipo Manter Apartamento ****************/
					/*****************************************************************/
					/*****************************************************************/
					case 1:
						break;
						
					case 2:
						List l = TipoApartamentoDao.listarApartamentos();
						Vector<TipoApartamento> v = new Vector<TipoApartamento>(l.size());
						for (int j = 0; j < l.size(); j++){
							v.add((TipoApartamento) l.get(j));
						}
						mta.setV(v);
						mta.setTipoManterTipoApartamento(21);
						try{
							mta.marshal(outstream);
						} catch (MarshallableException e) {
							e.printStackTrace();
						}
						
						try{
							os.write(outstream.getBytes());
						} catch (IOException ioe){
							ioe.printStackTrace();
						}
						
						break;
						
					default:
						break;
				}
				
				break;
				
			/*****************************************************************/
			/*****************************************************************/
			/************* Mensagem do tipo Manter Item Conta ****************/
			/*****************************************************************/
			/*****************************************************************/
			case TipoMensagem.MANTERITEMCONTA:
				// Cria uma nova mensagem do tipo recebido
				ManterItemConta mic = new ManterItemConta();
				
				// Lê os dados da mensagem recebida para a mensagem criada
				try {
					mic.unmarshal(stream);
				} catch (MarshallableException e) {
					e.printStackTrace();
				}
				
				// Analisa o subtipo da mensagem
				switch(mic.getSubTipo()){
				
				/*****************************************************************/
				/*****************************************************************/
				/******** Mensagem do tipo Inserir Serviço na Conta **************/
				/*****************************************************************/
				/*****************************************************************/
				case 1:
					ItemConta ic_ = mic.getI();
					Apartamento ap_conta = ApartamentoDao.apartamentoNumero(mic.getNumApto());
					System.out.println("Valor: "+mic.getI().getServico().getPreco());
					if(ap_conta != null){
						//System.out.println("Ap não é null!");
						/*
						// Cria uma nova entrada, para poder checar a ocupação 
						Entrada in_ = new Reserva();
						in_.setDataInicio(new Date());
						in_.setDataFim(new Date());
						in_.setAp(ap_conta);
						EntradaDao.checkOcupation(in_);
						*/
						
						// Verifica se o apartamento está realmente ocupado
						Registro e_ = (Registro) EntradaDao.checkOcupation(ap_conta.getNumero());
						//if((e_ != null)&&(e_ instanceof Registro)){
						if(e_ != null){
							// System.out.println("O apartamento estava ocupado mesmo");
							// Busca o serviço do item da conta
							// A lista nunca é vazia, pois os dados do serviço enviados já foram buscados no sistema 
							// (o combo box é preenchido com os dados do BD)
							List<Servico> ls_ = ServicoDao.buscarServico(ic_.getServico().getDescricao());
							
							// Seta o servico do item da conta
							ic_.setServico(ls_.get(0));
							
							// Salva o item da conta na memória
							ItemContaDao.create(ic_,e_.getConta());
							
							// Envia mensagem de confirmação
							mic.setSubTipo(11);
							
							// Envia o nome do Hospede
							// mic.setNomeHospede(e_.getHospedeId().getNome());
							// System.out.println("Nome do hospede: "+e_.getHospedeId().getNome());
							
							// List<Conta> l = ContaDao.listarServicos();
							// System.out.println("Itens da conta: "+l.size());
							
							try {
								mic.marshal(outstream);
								os.write(outstream.getBytes());
							} catch(IOException io){
								io.printStackTrace();
							} catch (MarshallableException e){
								e.printStackTrace();
							}
							
						} else {
							// Apartamento não está ocupado, envia mensagem de erro
							System.out.println("O apartamento não estava ocupado");
							mic.setSubTipo(12);
							try {
								mic.marshal(outstream);
								os.write(outstream.getBytes());
							} catch(IOException io){
								io.printStackTrace();
							} catch (MarshallableException e){
								e.printStackTrace();
							}
						}
						
					} else {
						// Se o apartamento não foi encontrado, envia mensagem indicando o erro
						mic.setSubTipo(13);
						try {
							mic.marshal(outstream);
							os.write(outstream.getBytes());
						} catch(IOException io){
							io.printStackTrace();
						} catch (MarshallableException e){
							e.printStackTrace();
						}
					}
					
					break;
				}
				break;
				
				
			/*****************************************************************/
			/*****************************************************************/
			/**************** Mensagem do tipo Manter Conta ******************/
			/*****************************************************************/
			/*****************************************************************/
			case TipoMensagem.MANTERCONTA:
				// Cria uma nova mensagem do tipo recebido
				ManterConta mc = new ManterConta();
				
				// Lê os dados da mensagem recebida para a mensagem criada
				try {
					mc.unmarshal(stream);
				} catch (MarshallableException e) {
					e.printStackTrace();
				}
				
				// Analisa o subtipo da mensagem
				switch(mc.getSubTipo()){
				/*****************************************************************/
				/*****************************************************************/
				/************* Mensagem do tipo Visualizar Conta *****************/
				/*****************************************************************/
				/*****************************************************************/
				case 1:
					Registro r_ = (Registro) EntradaDao.checkOcupation(mc.getNumAp());
					if(r_ == null){
						mc.setSubTipo(12);
					} else {
						mc.setSubTipo(11);
						List<ItemConta> l1 = ContaDao.listarServicos(r_.getConta());
						mc.setItensDaConta(l1);
					}
					try{
						mc.marshal(outstream);
						os.write(outstream.getBytes());
					} catch(IOException ioe){
						ioe.printStackTrace();
					} catch (MarshallableException mex){
						mex.printStackTrace();
					}
					break;
				}
				break;
				
			default:
				break;

		}
		
		try {
			os.close();
			is.close();
			socket.close();
		} catch (IOException i){
			
		}
	}
	
	public Socket getSocket() {
		return socket;
	}

	public void setSocket(Socket socket) {
		this.socket = socket;
	}
}
